package models;

import java.util.*;

import javax.persistence.*;

import play.data.validation.Required;
import play.db.jpa.*;
import javax.persistence.*;

/**
 * Entities Entity, Used to create an entity in a certain organization
 * 
 * @author Mai Abu-El Seoud
 */
@Entity
public class Entities extends Model {
	@OneToMany(mappedBy = "entity")
	public List<Permission> permission;
	/**
	 * The name of the entity
	 */
	@Required
	public String entityName;
	/**
	 * The status of the entity
	 */
	@Required
	public boolean isLocked;
	/**
	 * List of Organizers in the entity
	 */
	@ManyToMany
	public List<User> entityOrganizers;

	/**
	 * the entities has several number of User subscribed to it
	 * 
	 * @author Sara Assem
	 */
	@ManyToMany
	@JoinTable(name = "entitysubscribers")
	public List<User> subscribers;
	/**
	 * The relation is organization has many entities but entity belongs to only
	 * one organization
	 * 
	 * @author Mai Abu-El Seoud
	 */
	@ManyToOne
	public Organization organization;

	@OneToMany(mappedBy = "entity", cascade = CascadeType.ALL)
	public List<Topic> topics;
	/**
	 * the entity can have many relations and every relation belongs to more
	 * than one entity
	 * 
	 * @author Menna Magdy
	 */
	@ManyToMany(mappedBy = "entityList")
	public List<Relation> relationList;

	/**
	 * List of ideas that belong directly to the entity
	 * 
	 * @author Ghada Abu Shady
	 */

	@OneToMany(mappedBy = "entity", cascade = CascadeType.ALL)
	public List<Idea> AirIdeas;
	/**
	 * List of requests that belong to this entity
	 * 
	 * @author Shahir El Faramawy
	 */
	@OneToMany(mappedBy = "entityRequest", cascade = CascadeType.ALL)
	public List<Request> requestList;

	/**
	 * Default constructor
	 * 
	 * @param name
	 *            name of the entity
	 * @param status
	 *            The status of the relation
	 * @param org
	 *            The Organization which the entity belongs to
	 * @author Mai Abu-El Seoud
	 * @author Mai Selwanes only for log statement
	 */
	public Entities(String name, boolean status, Organization organization,
			User user)

	{
		this.entityName = name;
		this.isLocked = status;
		this.subscribers = new ArrayList<User>();
		// subscribers.addAll(entityOrganizers);
		this.organization = organization;

		this.AirIdeas = new ArrayList<Idea>();
		this.relationList = new ArrayList<Relation>();
		this.requestList = new ArrayList<Request>();
		this.topics = new ArrayList<Topic>();
		this.entityOrganizers = new ArrayList<User>();
		// this.save();
		Log log = new Log(this.organization, user,
				" has created a new Entity of title: " + name
						+ " in the organization.");
		log.save();

		organization.allEntities.add(this);
		organization.save();
		this.topics = new ArrayList<Topic>();
	}

	/**
	 * 
	 * This method takes an organization object and returns back the list of
	 * entities available in this organization
	 * 
	 * @param organization
	 *            This is the organization in which entities will be returned
	 * 
	 * @author Mai Abu-El Seoud
	 */

	public List<Entities> getOrgEntities(Organization organization) {
		List<Entities> allEntitiesList = Entities.findAll();
		List<Entities> organizationEntities = new LinkedList<Entities>();
		for (int i = 0; i < allEntitiesList.size(); i++) {
			if (allEntitiesList.get(i).organization == organization) {
				organizationEntities.add(allEntitiesList.get(i));
			}
		}
		return organizationEntities;
	}

	/**
	 * This method returns all the entities available on the system in a list
	 * 
	 * @ author Mai Abu-El Seoud
	 */

	public List<Entities> getEntities() {

		List<Entities> allEntitiesList = Entities.findAll();
		return allEntitiesList;
	}

	/*
	 * public boolean assignOrg(User user,Organization org){ String
	 * usertype=Permission.userPermissionInOrganization(user.id, org.id);
	 * if(Permission.usertype==usertype && usertype=="ideadeveloper") {
	 * Permission.usertype="organizer"; this.save(); subscribers.add(user);
	 * return true;
	 * 
	 * } else return false; }
	 */
	/**
	 * AddUsertoSubscribers is a method that takes a user as a param and adds it
	 * to the list of subscriber of this entity
	 * 
	 * @param User
	 *            that should be added
	 * 
	 * @author Sara Assem
	 * @author Mai Selwanes only for log statement
	 */

	public void addUserToSubscribers(User user) {
		subscribers.add(user);
		Log log = new Log(this.organization, user,
				" has been added to the list of subscribers of the entity of title: "
						+ this.entityName);
		log.save();
	}

	/**
	 * RemoveUserFromSubscribers is a method that takes a user as a param and
	 * removes it from the list of subscriber of this entity
	 * 
	 * @param User
	 *            that should be removed
	 * 
	 * @author Sara Assem
	 * @author Mai Selwanes only for log statement
	 */
	public void removeUserFromSubscribers(User user) {
		subscribers.remove(user);
		Log log = new Log(this.organization, user,
				" has been removed from the list of subscribers of the entity of title: "
						+ this.entityName);
		log.save();

	}

	/**
	 * sendInviteToBeOrg is a method that takes as params the sender and the
	 * list of receivers and using these, a new invitation to be an organizer
	 * will be created and an invitation will be added to the list of
	 * invitations available in the User
	 * 
	 * @param sender
	 *            the one who will send the invitation
	 * @param receiver
	 *            an array list of receivers
	 * 
	 * @author Mai Selwanes
	 */

	public void sendInviteToBeOrganizer(User sender, ArrayList<User> receiver) {
		String messageTemplate = sender.name
				+ " would like to invite you to be an organizer in Entity "
				+ this.entityName + " in " + this.organization.name;
		String type = "organizer";
		boolean hasBeenSentToReceiverBefore = false;
		for (int i = 0; i < receiver.size(); i++) {
			for (int m = 0; m < receiver.get(i).invitations.size(); m++) {
				if (receiver.get(i).invitations.get(m).entityOrOrgId == this.id
						&& receiver.get(i).invitations.get(m).message
								.equals(messageTemplate))

				{
					hasBeenSentToReceiverBefore = true;

				}

			}
			if (hasBeenSentToReceiverBefore == false) {
				Invitation invitation = new Invitation(receiver.get(i), messageTemplate,
						type, this.id);
				receiver.get(i).invitations.add(invitation);
				invitation.save();
			}

		}
	}

	/**
	 * sendInviteToBeID is a method that takes as params the sender and the list
	 * of receivers and using these, a new invitation to be an idea developer
	 * will be created and an invitation will be added to the list of
	 * invitations available in the User
	 * 
	 * @param sender
	 *            the one who will send the invitation
	 * @param receiver
	 *            an array list of receivers
	 * 
	 * @author Mai Selwanes
	 */

	// public void sendInviteToBeID(User sender, ArrayList<User> receiver) {
	// String msg = sender.name
	// + " would like to invite you to be an Idea Developer " + "in "
	// + this.organization.name;
	// String type = "organizer";
	// for (int i = 0; i < receiver.size(); i++) {
	// for (int m = 0; m < receiver.get(i).invitations.size(); m++) {
	// if (receiver.get(i).invitations.get(m).entityOrOrgId == this.id
	// && receiver.get(i).invitations.get(m).message == msg)
	//
	// {
	//
	// return;
	//
	// }
	//
	// }
	// Invitation invitation = new Invitation(receiver.get(i), msg, type,
	// this.id);
	// receiver.get(i).invitations.add(invitation);
	// invitation.save();
	// }
	// }

	public Entities getEntity(Entities e) {
		Long id = null;

		for (int i = 0; i <= getEntities().size(); i++) {
			if (e.equals(getEntities().get(i))) {
				id = getEntities().get(i).id;
			}
		}
		Entities req = Entities.findById(id);
		return req;
	}

	/**
	 * creates and return the idea that has been added directly to the
	 * entity. it notifies the subscribers to the entity with the new idea
	 * added.
	 * 
	 * @param author
	 *            the creator of the idea
	 * 
	 * @param title
	 *            title of the new idea
	 * 
	 * @param body
	 *            body of the new idea
	 * @return idea the idea that has been created
	 * @author Ghada Abu Shady
	 * @author Mai Selwanes only for log statement
	 * @author Heba Khattab incrementing user contribution counter by 1
	 */
	public Idea addIdeaToEntity(User author, String title, String body) {
		Idea idea = new Idea(author, title, body, this).save();
		this.AirIdeas.add(idea);
		String notificationBody = "" + author.name
				+ " has published the idea named " + title + "in the entity "
				+ idea.entity.entityName;
		author.sendNotificationWithType(notificationBody,
				idea.entity.subscribers, "Idea", idea.id);
		idea.entity.subscribers.add(author);
		Log log = new Log(this.organization, author,
				"has added an idea to the Organization's entity "
						+ this.entityName + "of title" + title);
		log.save();
		author.contributionCounter++;
		return idea;

	}

	/**
	 * takes Entity object as an input to lock it so nobody can construct a
	 * relation with this specific entity then save it to the database
	 * 
	 * @param takes
	 *            object entities as an object
	 * @author Mohamed Abdel Kader
	 * @author Mai Selwanes only for log statement
	 */
	public void lockEntity(Entities entity) {

		User userNull = null;
		Log log = new Log(this.organization, userNull, "Entity "
				+ entity.entityName + "has been locked.");
		log.save();
		entity.isLocked = true;
		entity.save();

	}

	/**
	 * takes Entity object as an input to unlock it so anybody can construct a
	 * relation with this specific entity then save it to the database
	 * 
	 * @param takes
	 *            object entities as an object
	 * 
	 * @author Mohamed Abdel Kader
	 * @author Mai Selwanes only for log statement
	 */

	public void unlockEntity(Entities entity) {

		User userNull = null;
		Log log = new Log(this.organization, userNull, "Entity "
				+ entity.entityName + "has been unlocked.");
		log.save();
		entity.isLocked = false;
		entity.save();

	}

	// public void AddUserToSubscribers(User user) {
	// subscribers.add(user);
	// }
	//
	// public void RemoveUserFromSubscribers(User user) {
	// subscribers.remove(user);
	// }

	/*
	 * public void blockIdeaDeveloperInEntity(User user,Entities e) { String
	 * usertype=Permission.userPermissionInOrganization(user.id, e.id);
	 * if(usertype=="ideaDeveloper") usertype="blocked";
	 * subscribers.remove(user.id); this.save(); }
	 */
	/**
	 * this method checks whether the user is in the subscriber list of the
	 * entities or not
	 * 
	 * 
	 * @param User
	 *            user the user that we want to check is he in the list or not .
	 * @author Sara Assem
	 */
	public boolean userinSubscribers(User user) {

		for (User subscriber : this.subscribers) {
			if (user == subscriber)
				return true;

		}
		return false;
	}

	public List<Topic> getTopics() {
		return topics;
	}

	/**
	 * loops on the entities in the organization and checks if the name entered
	 * by the user already exist.
	 * 
	 * 
	 * @param name
	 *            The new entity name entered by the user
	 * @param organization
	 *            The organization in which the entity will be created
	 * @return The method will return false , if the name already exists and
	 *         will return true, if the name doesn't exists
	 *  @author Mai Abu-El Seoud
	 */
	public static boolean checkName(String name, Organization organization) {

		if (organization.allEntities.size() >= 0) {
			for (int i = 0; i < organization.allEntities.size(); i++) {
				if (organization.allEntities.get(i).entityName
						.equalsIgnoreCase(name))
					return false;
			}
		}
		return true;
	}

	public List<User> getSubscribers() {
		// TODO Auto-generated method stub
		return this.subscribers;
	}

	/**
	 * Method search in Entites returns the list of entities with names that
	 * match the given string parameter search to search the keyword that will
	 * be used to be fetched from the database
	 * 
	 * @author Ahmed Ammar
	 */

	public static List<Entities> search(String search) {
		String likeSearch = search.toLowerCase();
		return Entities.find("LOWER(entityName) LIKE ?", likeSearch).fetch();
	}

	/**
	 * This method takes as an input the search entered and searches through the
	 * list of Entities for this search and returns the name of the entity
	 * entered and the number of times this name occurs
	 * 
	 * @param String
	 *            search @ Author Mohamed Ali
	 */
	public static SearchEntitiesResults searchForEntities(String search) {
		Long count = count("entityName like ?", search);
		List<Entities> entity1 = find("entityName like ?", search).fetch();
		return new SearchEntitiesResults(entity1, count);

	}

	/**
	 * returns a list of all topics in an entity that has no ideas
	 * 
	 * @author Nesreen elgohary
	 */
	public List<Topic> topicsWithNoIdeas() {
		List<Topic> unusedTopics = new LinkedList<Topic>();
		if (!this.topics.isEmpty()) {
			for (int counter = 0; counter < this.topics.size(); counter++) {
				if (this.topics.get(counter).ideas.isEmpty()) {
					unusedTopics.add(this.topics.get(counter));
				}
			}
		}
		return unusedTopics;
	}

	/**
	 * returns a list of all topics in an entity that has hide status true
	 * 
	 * @author Nesreen elgohary
	 */
	public List<Topic> listOfHiddenTopics() {
		List<Topic> hiddenTopics = new LinkedList<Topic>();
		for (int counter = 0; counter < this.topics.size(); counter++) {
			Topic topic = this.topics.get(counter);
			if (topic.hide == true) {
				hiddenTopics.add(topic);
			}
		}
		return hiddenTopics;
	}

	public static Long compareNames(String name, Long organizationId) {
		Organization organization = Organization.findById(organizationId);
		List<Entities> entities = organization.getEntities();
		Long id = null;
		for (int i = 0; i < entities.size(); i++) {
			System.out.println(entities.get(i).id);
			System.out.println(name);
			if (entities.get(i).entityName.equalsIgnoreCase(name))

				id = entities.get(i).id;

		}
		System.out.println(id);
		return id;

	}

	/**
	 * This method takes the user id and returns true if the type of the
	 * organization that he's searching in is public , OR False if the
	 * organization is private/Closed and he's Not a member in it and True
	 * otherwise
	 * 
	 * @param userId
	 *            the user's id
	 * @return returns the True or False
	 * @author Nourhan El-Nehlawy
	 */
	public boolean checkType(Long userId) {
		if (!this.organization.type.matches("Public")) {
			User user = User.findById(userId);
			if (this.organization.checkUser(user)) {
				return true;
			} else
				return false;
		} else
			return true;
	}
}
